package com.icloud.wallet.boot.schedule;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.icloud.wallet.boot.constant.WalletConstants;
import com.icloud.wallet.boot.utils.BNBWeb3jApiUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.methods.response.EthBlock;
import org.web3j.protocol.core.methods.response.Log;
import org.web3j.protocol.core.methods.response.Transaction;
import org.web3j.protocol.core.methods.response.TransactionReceipt;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;

// 币安扫块任务
@Component
@Slf4j
public class BNBScanTask {
    @Resource
    private Web3j web3Client;
    private final static String method_id = "0xa9059cbb";//BNB代币 合约转账方法id

    public void scanBlock(){
        BigInteger blockNumber = new BigInteger("1");
        //当前区块高度 钱包服务器区块高度
        BigInteger lastblockNumber = null;
        try {
            //Thread.sleep(800);//休眠800毫秒，防止频繁请求
            lastblockNumber = BNBWeb3jApiUtil.getBlockNumber(web3Client);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return;
        }

        if (lastblockNumber.subtract(blockNumber).intValue() < 10) {
            log.info("相差区块个数不够，结束以下代码执行，等待下一次检查，链上区块高度：{}，本地数据库区块高度：{}", lastblockNumber, blockNumber);
            return;
        }

        //获取指定区块交易详情，包含该区块下的所有交易记录
        List<EthBlock.TransactionResult> transactionsList = null;
        try {
            transactionsList = web3Client.ethGetBlockByNumber(DefaultBlockParameter.valueOf(blockNumber), true).send().getBlock().getTransactions();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return;
        }

        log.info("--------------正在同步 BNB 区块高度，当前钱包服务器区块【{}】，系统同步区块【{}】，系统同步区块交易笔数【{}】---------------", lastblockNumber, blockNumber, transactionsList.size());

        String collectAddress = WalletConstants.COLLECT_ADDRESS; //归集地址
        String contractAddress = WalletConstants.CONTRACT_ADDRESS_USDT;//手续费（平台币）合约地址
        int coinDecimal = 18;//手续费（平台币）精度

        if (ObjectUtil.isEmpty(transactionsList)) {
            log.info("--------------正在同步 BNB 区块高度,当前节点链上区块【{}】,正在处理区块【{}】，该区块没有交易记录---------------", lastblockNumber, blockNumber);
        } else {
            for (EthBlock.TransactionResult transactionResult : transactionsList) {
                EthBlock.TransactionObject tx = (EthBlock.TransactionObject) transactionResult;
                Object input = tx.getInput();
                if (input != null && StringUtils.equals((String) input, "0x6f6178")) {//如果input为0x6f6178时为申请手续费
                    //说明是内部转账
                    continue;
                }

                String to = tx.getTo();//交易详情中-收地址
                if (StrUtil.isEmpty(to)) {
                    //收款地址为空的
                    continue;
                }

                if (to.equalsIgnoreCase(collectAddress)) {
                    //交易详情中的收款地址 = 手续费归集地址，则说明为转的主币（BNB），就不处理
                    continue;
                }

                //检查交易记录-如果是直接转账到归集地址
                if (to.equalsIgnoreCase(contractAddress)) {
                    //交易详情中的收款地址 = 合约地址，则说明为转的代币

                    //判断当前交易记录中的收款地址，是否为平台的 交易手续费-归集地址
                    if (EBP20ToAdderssIsOk((String) input, collectAddress, coinDecimal)) {
                        //核对无误，说明有交易手续费 转入到 归集地址
                        if (insertBNBTokenRecharge(tx, (String) input, coinDecimal)) {
                            continue;
                        }
                    }
                }else{
                    //检查交易事件中的记录
                    if (insertBNBTokenRechargeByHash(tx,coinDecimal,contractAddress,collectAddress)){
                        continue;
                    }
                }
            }
        }
    }

    /**
     * <p>方法说明: 判断当前交易记录中的收款地址，是否为交易手续费-归集地址
     * <p>参数说明:
     * <p>返回说明: false:不存在，true：存在
     * <p>创建时间: 2021/2/22 0022 10:33
     * <p>创  建  人: java-101
     **/
    private boolean EBP20ToAdderssIsOk(String input, String inFreeAddress, int coinDecimal) {
        /*
        TransactionDetails transactionDetails = new TransactionDetails();
        try {
            //分割 data获取 toaddress（转入地址）,qty（充值数量/转入数量）
            partitionInputDataAndSetRecharge(input, method_id, coinDecimal, transactionDetails);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return false;
        }

        String toAddress = transactionDetails.getToAddress();
        if (StrUtil.isEmpty(toAddress)) {
            return false;
        }

        //平台手续费归集地址不为空，并等于交易详情中的收款地址
        if (StrUtil.isNotEmpty(inFreeAddress) && inFreeAddress.equalsIgnoreCase(toAddress)) {
            return true;
        } else {
            return false;
        }*/
        return true;
    }

    /**
     * <p>方法说明: 分割 input获取 toaddress（转入地址）,qty（充值数量/转入数量）
     * <p>参数说明:
     * <p>返回说明:
     * <p>创建时间: 2020/6/23 0023 14:06
     * <p>创  建  人: java-101
     **/
    private void partitionInputDataAndSetRecharge(String input, String method_id, Integer coinDecimal/*, TransactionDetails transactionDetails*/) throws Throwable {
        String inputData = input;
        //拆分合约数据
        inputData = inputData.replace(method_id, "");//MethodId：合约转账方法id
        String toAddressInfo = inputData.substring(0, inputData.length() / 2);
        String moneyInfo = inputData.substring(inputData.length() / 2);
        String toAddress = BNBWeb3jApiUtil.getridof_zero_address(toAddressInfo);
        String moneyInfo_zero_end = BNBWeb3jApiUtil.getridof_zero(moneyInfo);

        BigDecimal money = new BigDecimal(BNBWeb3jApiUtil.toNum(moneyInfo_zero_end.equals("0x") ? "0x0" : moneyInfo_zero_end).toString());
        BigDecimal realMoney = money.divide(new BigDecimal(10).pow(coinDecimal));

        //transactionDetails.setToAddress(toAddress);//转入地址
        //transactionDetails.setQty(realMoney);//充值数量
    }

    /**
     * <p>方法说明: 添加BNB_TOKEN链上余额记录到数据库
     * <p>参数说明:
     * <p>返回说明:
     * <p>创建时间: 2020/6/23 0023 13:59
     * <p>创  建  人: java-101
     **/
    private boolean insertBNBTokenRecharge(Transaction tx, String input, int coinDecimal) {
        String hash = tx.getHash();
        TransactionReceipt txReceipt = null;
        try {
            txReceipt = BNBWeb3jApiUtil.getTransactionReceipt(web3Client, hash);
        } catch (Exception e) {
            e.printStackTrace();
            return true;
        }
        Object status = txReceipt.getStatus();//// status为1表示成功
        if (status == null) {
            //转账未确定
            return true;
        }
        BigInteger bigInteger = BNBWeb3jApiUtil.toNum((String) status);
        if (bigInteger.intValue() != 1) {
            //转账不成功
            return true;
        }

        //将解析得到的收款地址和转账数量，存入其中
        /*
        TransactionDetails transactionDetails = new TransactionDetails();

        try {
            //分割 data获取 toaddress（转入地址）,qty（充值数量/转入数量）
            partitionInputDataAndSetRecharge(input, method_id, coinDecimal, transactionDetails);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return true;
        }

        //判断充值数量是否有效
        if (transactionDetails.getQty().compareTo(BigDecimal.ZERO) <= 0) {
            log.info("【归集转账不符合条件】，归集收款地址【{}】，转账数量【{}】", transactionDetails.getToAddress(), transactionDetails.getQty().stripTrailingZeros().toPlainString());
            return true;
        }

        String from = tx.getFrom();
        transactionDetails.setFromAddress(from);
        transactionDetails.setTxId(hash);
         */

        return false;
    }

    /**
     * 方法说明: 检查交易事件中的记录
     * 传入参数:
     * 返回的值:
     * 作者: java-101
     * 日期: 2024-05-24 19:42:53
     */
    private boolean insertBNBTokenRechargeByHash(Transaction tx,int coinDecimal,String contractAddress,String inFreeAddress){

        String hash = tx.getHash();

        TransactionReceipt txReceipt = null;
        try {
            txReceipt = BNBWeb3jApiUtil.getTransactionReceipt(web3Client, hash);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(ObjectUtil.isEmpty(txReceipt)){
            return true;
        }
        List<Log> list = txReceipt.getLogs();
        if(ObjectUtil.isEmpty(list)){
            return true;
        }
        for (Log logInfo : list) {
            String logContractAddress = logInfo.getAddress();
            if(!logContractAddress.equalsIgnoreCase(contractAddress)){
                continue;
            }
            //只处理自己的代币合约地址
            List<String> topics = logInfo.getTopics();

            String fromAddress = null; // 第二个主题即发送者地址
            String toAddress = null; // 第三个主题即接收者地址
            BigDecimal realMoney = BigDecimal.ZERO;//数量

            if (topics.size() > 0) {
                try {
                    // 获取发送者地址
                    fromAddress = topics.get(1);
                    fromAddress = BNBWeb3jApiUtil.getridof_zero_address(fromAddress);

                    // 获取接收者地址
                    toAddress = topics.get(2);
                    toAddress = BNBWeb3jApiUtil.getridof_zero_address(toAddress);

                    // 获取交易数量
                    String moneyInfo = logInfo.getData();
                    moneyInfo = moneyInfo.length()>64?moneyInfo.replace("0x",""):moneyInfo;
                    String moneyInfo_zero_end = BNBWeb3jApiUtil.getridof_zero(moneyInfo);
                    BigDecimal money = new BigDecimal(BNBWeb3jApiUtil.toNum(moneyInfo_zero_end.equals("0x") ? "0x0" : moneyInfo_zero_end).toString());
                    realMoney = money.divide(new BigDecimal(10).pow(coinDecimal));
                } catch (Throwable e) {
                    throw new RuntimeException(e);
                }
                System.out.println("合约地址："+logContractAddress+"，fromAddr = "+fromAddress+"，toAddr = "+toAddress+"，数量 = "+ realMoney.stripTrailingZeros().toPlainString());
            }

            if(StrUtil.isEmpty(fromAddress) || StrUtil.isEmpty(toAddress)){
                log.info("【归集转账不符合条件】，from地址为空或to地址为空");
                continue;
            }

            if(!toAddress.equalsIgnoreCase(inFreeAddress)){
                log.info("【归集转账不符合条件】，记录中收款地址【{}】，并不是平台归集地址，记录中转账数量【{}】",toAddress,realMoney.stripTrailingZeros().toPlainString());
                continue;
            }

            if(realMoney.compareTo(BigDecimal.ZERO) <= 0){
                log.info("【归集转账不符合条件】，归集收款地址【{}】，转账数量【{}】", toAddress, realMoney.stripTrailingZeros().toPlainString());
                continue;
            }
        }
        return false;
    }
}
